import type { FC } from 'react';
import { useMemoizedFn } from 'ahooks';
import { useRef, useEffect, memo } from 'react';
import { Scrollbars as RcScrollbars } from 'rc-scrollbars';
import { getPosition, scrollTo } from './helper';
import type { PosType } from '../types';

type ScrollbarsProps = {
  children?: any;
}

type MoveData = {
  xd: string;
  yd: string;
  x: number;
  y: number;
}

const Scrollbars: FC<ScrollbarsProps> = (props) => {
  const ref = useRef<RcScrollbars>(null);

  const getScroller = useMemoizedFn(() => {
    return ref.current as RcScrollbars;
  });

  const getElement = useMemoizedFn(() => {
    const s = getScroller();
    return s.container as HTMLDivElement;
  });

  const isContains = useMemoizedFn((evt: MouseEvent) => {
    const ele = evt.target as Element;
    return getElement().contains(ele);
  });

  const getMousePos = useMemoizedFn((evt: MouseEvent) => {
    return getPosition(evt, getElement());
  });

  const initContrl = useMemoizedFn(() => {
    let prev: PosType | null;
    let flag: boolean = false;
    let timer1: NodeJS.Timer | null;
    let timer2: NodeJS.Timer | null;

    const container = getElement();

    const clearTimer = () => {
      if (timer1) {
        clearInterval(timer1);
        timer1 = null;
      }
      if (timer2) {
        clearInterval(timer2);
        timer2 = null;
      }
    };

    const scrollMove = (evt: MoveData) => {
      const boundary = 20;
      const s = getScroller();
      const { x, y, xd, yd } = evt;

      const xb = x <= boundary || (s.getClientWidth() - x) <= boundary
      const yb = y <= boundary || (s.getClientHeight() - y) <= boundary;

      if (xb) {
        timer1 = scrollTo(s.scrollLeft, {
          reduce: xd === 'left',
          start: s.getScrollLeft(),
          end: s.getScrollWidth(),
        });
      }
      if (yb) {
        timer2 = scrollTo(s.scrollTop, {
          reduce: yd === 'top',
          start: s.getScrollTop(),
          end: s.getScrollHeight(),
        });
      }
    }

    const onMouseMove = (evt: MouseEvent) => {
      if (!flag) {
        return;
      }
      const boundary = 20;
      const preX = prev?.x || 0;
      const preY = prev?.y || 0;
      const next = getMousePos(evt);

      const diffX = next.x - preX;
      const diffY = next.y - preY;

      if (Math.abs(diffX) >= boundary || Math.abs(diffY) >= boundary) {
        prev = next;
        const xd = diffX > 0 ? 'right' : 'left';
        const yd = diffY > 0 ? 'bottom' : 'top';
        clearTimer();
        scrollMove({ xd, yd, x: next.x, y: next.y });
      }
    }

    // 抬起鼠标
    const onMouseUp = () => {
      flag = false;
      clearTimer();
    }

    //按下鼠标
    const onMouseDown = (evt: MouseEvent) => {
      if (flag = isContains(evt)) {
        prev = null;
        clearTimer();
      };
    }

    document.addEventListener('mouseup', onMouseUp);
    container.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mousedown', onMouseDown);
    document.addEventListener('contextmenu', onMouseUp);

    return () => {
      clearTimer();
      document.removeEventListener('mouseup', onMouseUp);
      container.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mousedown', onMouseDown);
      document.removeEventListener('contextmenu', onMouseUp);
    }
  });

  useEffect(initContrl, [initContrl]);

  return (
    <RcScrollbars
      ref={ref}
      autoHide
      autoHideTimeout={500}
    >
      {props.children}
    </RcScrollbars>
  );
}

export default memo(Scrollbars);